#include "Socket.hpp"
#include "Channel.hpp"  //注意Channel和Epoller的顺序 不能交换！
#include "Epoller.hpp"
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <vector>

//这里为了方便测试，我们手动地为普通套接字的回调编写一下！
void HandlerClose(Channel* ch){
//关闭套接字的回调：不仅仅是关闭连接，还要先取消关注！
    std::cout << "close the fd : " << ch->Fd() << std::endl;
    ch->Remove();
    close(ch->Fd());
    /*注意这个位置：直接断开连接：在现有的channel处理事件逻辑下，是先处理读写再处理任意的事件的*/
    /*但是我们这里一旦出现了读写错误就释放连接，析构channel，对象就没有了！ 后序在触发任意事件的时候就出现野指针使用了！*/
    /*实际上：到后续的业务中 读出错不关闭连接，因为要确保接收缓冲区的内容数据全部读完！*/
    
    //所以这就是为什么当前一旦有一个客户端退出，直接段错误了！！！！！
    /*所以我们还是去channel的回调处理逻辑进行修改*/
    delete ch;
    ch = nullptr;
}

//这个是站在服务端视角来看fd是否可读
void HandlerRead(Channel* ch){
    char buffer[1024] = {0};
    int n = recv(ch->Fd(), buffer, sizeof(buffer) - 1, 0);
    //实际上，不应该是直接挂断，应该是先把错误事件加入关心！
    if(n <= 0) HandlerClose(ch);
    else{
        std::cout << buffer << std::endl;
        //设置写事件的监控
        ch->SetWriteEventMonitor();
    }
}

//这个是站在服务端视角来看fd是否可写
void HandlerWrite(Channel* ch){
    //真正要发送的信息，其实是通过Connection模块的接收缓冲区中读取出来的！然后处理在进行发送
    //这里为了方便测试，直接给一个固定消息即可
    const char* message = "This is a test";
    int n = send(ch->Fd(), message, strlen(message), 0);
    if(n <= 0) HandlerClose(ch);
    else{
        //发送成功了！ -> 关闭写就绪
        ch->DelWriteEventMonitor();
    }
}

//错误事件回调也直接关闭即可
void HandlerError(Channel* ch){HandlerClose(ch);}

void HandlerAny(Channel* ch){
    std::cout << "any" << std::endl;
}

//这个是listen套接字的读回调，其实就是获取新的连接
//功能就是：获取新的连接，然后对新的连接进行设置(监控、回调等)！
void Acceptor(Channel* lis_ch, Epoller* ep){
    //这里就先不用Socket了！因为这里面用的话就是临时变量，一旦退出函数析构了，自动断开连接了!
    //到时候在其他模块会使用Socket，这里为了测试，先写的原生一点
    int lisfd = lis_ch->Fd();
    int fd = accept(lisfd, nullptr,nullptr);
    if(fd < 0) return; //获取链接都失败了，直接返回就好了！

    //开始设置新连接的回调！
    Channel* newch = new Channel(fd, ep);
    newch->ReadCallback_Set(std::bind(HandlerRead, newch));
    newch->WriteCallback_Set(std::bind(HandlerWrite, newch));
    newch->ErrorCallback_Set(std::bind(HandlerError, newch));
    newch->CloseCallback_Set(std::bind(HandlerClose, newch));
    newch->AnyCallback_Set(std::bind(HandlerAny, newch));

    //对于一个套接字来说，读事件默认不就绪，所以需要关心！
    newch->SetReadEventMonitor();
}


// ./test_svr.exe server_port
int main(int argc, char* argv[]){
    //if(argc != 2) return -1;

    //创建套接字
    uint16_t server_port = std::stoi(argv[1]);
    Socket server_socket;
    server_socket.CreateServer(8080/*server_port*/);

    Epoller epoller;
    //专门用于listen套接字的事件管理
    Channel listen_channel(server_socket.Fd(), &epoller);
    //对于连接套接字来说，只需要设置一个读事件回调即可！(方便测试)
    listen_channel.ReadCallback_Set(std::bind(Acceptor, &listen_channel, &epoller));
    //启动监听套接字的读事件监控
    listen_channel.SetReadEventMonitor();

    while(1){
        std::vector<Channel*> active;
        epoller.EpollWait(active);

        //根据返回的就绪的事件进行处理！
        for(auto& a : active) std::cout << a->Fd() << std::endl;
        sleep(1);

        for(auto& a : active) a->HandlerRevents();
        std::cout << "while(1)" << std::endl;
    }
    return 0;
}